package me.lubinn.Vicincantatio.Spells;

import org.bukkit.util.Vector;

abstract class Area
{
	//parameters for AffectedArea
	protected double sizeBase = 4;
	protected double sizeGrowth = 3;
	
	public Area() { }
	public Area(double base, double growth) 
	{
		sizeBase = base;
		sizeGrowth = growth;
	}
	
	public int Size(int size) //gets the size of the cube to iterate over, given a spell size
	{
		return (int)(sizeBase + size*sizeGrowth);
	}
	public int boundXLow(int size)
	{
		return -Size(size);
	}
	public int boundXHi(int size)
	{
		return Size(size);
	}
	public int boundYLow(int size)
	{
		return -Size(size);
	}
	public int boundYHi(int size)
	{
		return Size(size);
	}
	public int boundZLow(int size)
	{
		return -Size(size);
	}
	public int boundZHi(int size)
	{
		return Size(size);
	}
	public boolean InY(int size, int dx, int dz) //gets whether there are any points in a given x,z column
	{
		return true; //default to true if not overwritten
	}
	public abstract boolean In(int size, int dx, int dy, int dz); //gets whether a point is in the area, given that the center of the area is (0, 0, 0)
	public abstract boolean OnBorder(int size, int dx, int dy, int dz); //gets whether a point is on the region border
	public void Instantiate(Vector heading) { } //set up an area with a given direction
}

class Cube extends Area
{
	public Cube() { }
	public Cube(double base, double growth)  
	{
		super(base, growth);
	}
	
	public boolean In(int size, int dx, int dy, int dz)
	{
		int s = Size(size);
		return Math.abs(dx) <= s && Math.abs(dy) <= s && Math.abs(dz) <= s;
	}	
	public boolean OnBorder(int size, int dx, int dy, int dz)
	{
		int s = Size(size);
		return Math.abs(dx) == s || Math.abs(dy) == s || Math.abs(dz) == s;
	}
}
class Sphere extends Area
{
	public Sphere() { }
	public Sphere(double base, double growth) 
	{
		super(base, growth);
	}
	
	public int boundXLow(int size)
	{
		return -Size(size)-1;
	}
	public int boundXHi(int size)
	{
		return Size(size)+1;
	}
	public int boundYLow(int size)
	{
		return -Size(size)-1;
	}
	public int boundYHi(int size)
	{
		return Size(size)+1;
	}
	public int boundZLow(int size)
	{
		return -Size(size)-1;
	}
	public int boundZHi(int size)
	{
		return Size(size)+1;
	}	
	public boolean InY(int size, int dx, int dz)
	{
		int r = Size(size);
		return (int)Math.sqrt(dx*dx + dz*dz) <= r;
	}
	public boolean In(int size, int dx, int dy, int dz)
	{
		int r = Size(size);
		return (int)Math.sqrt(dx*dx + dy*dy + dz*dz) <= r;
	}	
	public boolean OnBorder(int size, int dx, int dy, int dz)
	{
		int r = Size(size);
		return (int)Math.sqrt(dx*dx + dy*dy + dz*dz) == r;
	}	
}
class HollowSphere extends Sphere
{
	public HollowSphere() { }
	public HollowSphere(double base, double growth)
	{
		super(base, growth);
	}
	
	public boolean In(int size, int dx, int dy, int dz)
	{
		int r = Size(size);
		int d = (int)Math.sqrt(dx*dx + dy*dy + dz*dz);
		return d <= r && d >= 3;
	}
}
class UpperHemisphere extends Sphere
{
	public UpperHemisphere() { }
	public UpperHemisphere(double base, double growth)
	{
		super(base, growth);
	}
	
	public int boundYLo(int size)
	{
		return 0;
	}
	public boolean OnBorder(int size, int dx, int dy, int dz)
	{
		int r = Size(size);
		return (int)Math.sqrt(dx*dx + dy*dy + dz*dz) == r || (dy == 0 && (int)Math.sqrt(dx*dx + dz*dz) <= r); 
	}
}
class LowerHemisphere extends Sphere
{
	private double height;
	
	public LowerHemisphere() { }
	public LowerHemisphere(double base, double growth) 
	{
		this(base, growth, 1.0);
	}
	public LowerHemisphere(double base, double growth, double height)
	{
		super(base, growth);
		this.height = height;
	}

	public int boundYLo(int size)
	{
		return (int)(-Size(size)*height) - 1;
	}
	public int boundYHi(int size)
	{
		return 0;
	}
	public boolean InY(int size, int dx, int dz)
	{
		int r = Size(size);
		return (int)Math.sqrt(dx*dx + dz*dz) <= r;
	}
	public boolean In(int size, int dx, int dy, int dz)
	{
		int r = Size(size);
		double b = 1.0/height;
		return (int)Math.sqrt(dx*dx + b*b*dy*dy + dz*dz) <= r; 
	}
	public boolean OnBorder(int size, int dx, int dy, int dz)
	{
		int r = Size(size);
		double b = 1.0/height;
		return (int)Math.sqrt(dx*dx + b*b*dy*dy + dz*dz) == r || (dy == 0 && (int)Math.sqrt(dx*dx + dz*dz) <= r); 
	}
}
class Cylinder extends Area
{
	private double radBase = 2;
	private double radGrowth = 4;
	
	public Cylinder() { }
	public Cylinder(double rBase, double rGrowth, double hBase, double hGrowth)
	{
		super(hBase, hGrowth); //Size(..) for radius of tower
		radBase = rBase;
		radGrowth = rGrowth;
	}
	
	public int Radius(int size)
	{
		return (int)(radBase + radGrowth*size);
	}
	public boolean In(int size, int dx, int dy, int dz)
	{
		int h = Size(size);
		int r = Radius(size);
		return (int)Math.sqrt(dx*dx + dz*dz) <= r && Math.abs(dy) <= h;
	}
	public boolean OnBorder(int size, int dx, int dy, int dz)
	{
		int h = Size(size);
		int r = Radius(size);
		return (int)Math.sqrt(dx*dx + dz*dz) == r || (Math.abs(dy) == h && (int)Math.sqrt(dx*dx + dz*dz) <= r);
	}
}
class Ring extends Sphere
{
	public Ring() { }
	public Ring(double base, double growth)
	{
		super(base, growth);
	}
	
	public int boundYLow(int size)
	{
		return -Size(size)/2;
	}
	public int boundYHi(int size)
	{
		return Size(size)/2;
	}
	public boolean In(int size, int dx, int dy, int dz)
	{
		int r = Size(size);
		return (int)Math.sqrt(dx*dx + dz*dz) <= r;
	}
	public boolean OnBorder(int size, int dx, int dy, int dz)
	{
		int r = Size(size);
		return (int)Math.sqrt(dx*dx + dz*dz) == r;
	}
}
class Disk extends Ring
{
	public Disk() { }
	public Disk(double base, double growth)
	{
		super(base, growth);
	}
	
	public int boundYLow(int size)
	{
		return 0;
	}
	public int boundYHi(int size)
	{
		return 0;
	}
}
class Point extends Area
{
	public int boundXLow(int size)
	{
		return 0;
	}
	public int boundXHi(int size)
	{
		return 0;
	}
	public int boundYLow(int size)
	{
		return 0;
	}
	public int boundYHi(int size)
	{
		return 0;
	}
	public int boundZLow(int size)
	{
		return 0;
	}
	public int boundZHi(int size)
	{
		return 0;
	}
	public boolean In(int size, int dx, int dy, int dz)
	{
		return true;
	}
	public boolean OnBorder(int size, int dx, int dy, int dz)
	{
		return true;
	}
}
class Platform extends Point
{
	public int boundXLow(int size)
	{
		return -1;
	}
	public int boundXHi(int size)
	{
		return 1;
	}
	public int boundZLow(int size)
	{
		return -1;
	}
	public int boundZHi(int size)
	{
		return 1;
	}
}
class Tetrahedron extends Area
{
	static final double phi = 109.47122055; //tetrahedral angle
	static final double h = Math.cos((180-phi)*Math.PI/180); //y distance from center to lower points
	static final double r = Math.sin((180-phi)*Math.PI/180); //xz distance from center to lower points
	
	protected Vector A, B, C, D; //the points
	protected Vector N1, N2, N3, N4; //the normals

	public Tetrahedron() { }
	public Tetrahedron(double base, double growth)
	{
		super(base, growth);
	}
	
	public void Instantiate(Vector heading)
	{
		double xz = Math.sqrt(heading.getX()*heading.getX() + heading.getZ()*heading.getZ());
		//double theta = Math.asin(heading.x);
		
		A = new Vector(0, 1, 0);
		B = new Vector();
	}
	public boolean In(int size, int dx, int dy, int dz) 
	{
		// TODO Auto-generated method stub
		return false;
	}
	public boolean OnBorder(int size, int dx, int dy, int dz) 
	{
		// TODO Auto-generated method stub
		return false;
	}

}